﻿using System;
using Framework.Repository;
using NHibernate;
using Rhino.Mocks;
using Rhino.Mocks.Interfaces;
using Xunit;

namespace Framework.Tests.Repository
{
    public class EntityTest
    {
        public virtual int Id { get; protected set; }
        public virtual string Name { get; set; }
    }

    public class EntityRepositoryTest
    {
        private readonly IRepository<EntityTest> target;
        private readonly MockRepository mockRepository;

        public EntityRepositoryTest()
        {
            mockRepository = new MockRepository();
            sessionFactoryMock = mockRepository.StrictMock<ISessionFactory>();
            sessionMock = mockRepository.StrictMock<ISession>();
            transactionMock = mockRepository.StrictMock<ITransaction>();
            target = new Repository<EntityTest>(sessionFactoryMock);
        }

        private readonly ISessionFactory sessionFactoryMock;

        private readonly ISession sessionMock;

        private readonly ITransaction transactionMock;

        [Fact]
        public void When_QueryOver_Entity_Transaction_is_commited()
        {
            var entity = new EntityTest {Name = "Entity"};

            MockTransaction(entity, false);
            mockRepository.ReplayAll();
            target.Save(entity);
            mockRepository.VerifyAll();
        }

        [Fact]
        public void When_Query_on_entity_if_there_is_failure_transaction_is_rollbacked()
        {
            var entity = new EntityTest {Name = "Entity"};
            MockTransaction(entity, true);
            mockRepository.ReplayAll();
            Assert.Throws<RepositoryException>(() => target.Save(entity));
            mockRepository.VerifyAll();
        }

        [Fact]
        public void Should_run_query()
        {
            var entity = new EntityTest {Name = "Entity"};
            MockTransaction(entity, false);
            mockRepository.ReplayAll();
            target.Save(entity);
            mockRepository.VerifyAll();
        }

        private void MockTransaction(EntityTest entity, bool throwException)
        {
            var sessionExpect = MockSave(entity);
            sessionMock.Expect(x => x.BeginTransaction()).Return(transactionMock);

            if (throwException)
            {
                sessionExpect.Throw(new NotImplementedException("Crash ta gueule!"));
                transactionMock.Expect(x => x.Rollback());
            }

            else
            {
                transactionMock.Expect(x => x.Commit());
            }
            transactionMock.Expect(x => x.Dispose());
        }

        private IMethodOptions<RhinoMocksExtensions.VoidType> MockSave(EntityTest entity)
        {
            var sessionExpect = sessionMock.Expect(x => x.SaveOrUpdate(entity));

            sessionFactoryMock.Expect(x => x.OpenSession()).Return(sessionMock);
            sessionMock.Expect(x => x.Dispose());
            return sessionExpect;
        }
    }
}